(function (global, factory) {
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
  typeof define === 'function' && define.amd ? define([], factory) :
  global.animation = factory();
}(this, function () { 'use strict';

const FRAME60 = 1000 / 60;
const STATIC_FRAME = 'STATIC_FRAME';
const ANIMATION_ONCE = 'ANIMATION_ONCE';
const ANIMATION_LOOP = 'ANIMATION_LOOP';

(function() {
  var lastTime = 0;
  var vendors = ['webkit', 'moz'];
  for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
    window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'];
    window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] ||    // Webkit中此取消方法的名字变了
                                  window[vendors[x] + 'CancelRequestAnimationFrame'];
  }

  if (!window.requestAnimationFrame) {
    window.requestAnimationFrame = function(callback, element) {
      var currTime = new Date().getTime();
      var timeToCall = Math.max(0, FRAME60 - (currTime - lastTime));
      var id = window.setTimeout(function() {
        callback(currTime + timeToCall);
      }, timeToCall);
      lastTime = currTime + timeToCall;
      return id;
    };
  }
  if (!window.cancelAnimationFrame) {
    window.cancelAnimationFrame = function(id) {
      clearTimeout(id);
    };
  }
}());


//修改easingFuc方法只计算单位周期内的值, 不再把delay计算在内
var easingMap = {
  liner: function(config) {
    return function(t) {
      return config.start + (config.end - config.start) * t / config.duration;
    }
  }
}

var animation = function(option) {
  option.delay = option.delay || 0;
  option.duration = option.duration || 200;
  option.easing = option.easing || 'liner';
  var propsOption = {};

  for (var param in option.props) {
    var val = option.props[param];
    if (typeof val === 'object') {
      propsOption[param] = val;
    } else {
      propsOption[param] = {
        start: 0,
        end: val
      }
    }

    var propOption = propsOption[param]

    propOption.start = propOption.start || 0;
    propOption.delay = propOption.delay || option.delay;
    propOption.duration = propOption.duration || option.duration;
    propOption.easing = propOption.easing || option.easing;
    propOption.easingFuc = (propOption.getEasingFuc || easingMap[propOption.easing])(propOption);
    propOption.complete = propOption.complete || option.complete;
  }

  this.option = option;
  this.propsOption = propsOption;
}

function getStaticFrame(t) {
  var option = this.option;
  var propsOption = this.propsOption;

  var animationObj = {};
  for (var param in propsOption) {
    var propOption = propsOption[param];
    var start = propOption.start;
    var end = propOption.end;
    animationObj[param] = {
      config: propOption,
      start: start,
      end: end
    }

    if (t < propOption.delay) { //动画未到时间

      animationObj[param].now = propOption.start;

    } else if (t <= propOption.delay + propOption.duration) {

      if (!animationObj[param].started) {
        animationObj[param].started = true
      }

      animationObj[param].now = easing(propOption, t - propOption.delay);
      
    } else {

      if (propOption.infinity || propOption.backtrace) { //带返程的循环动画

        if (propOption.infinity && propOption.backtrace) {
          const time = t - propOption.delay;
          const timeCount = parseInt(time / propOption.duration);
          const timeExtra = time % propOption.duration;

          if (!animationObj[param].started) {
            animationObj[param].started = true
          }

          if (timeCount % 2 === 0) { //正向变化时期
            animationObj[param].now = easing(propOption, timeExtra);
          } else { //回溯时期
            animationObj[param].now = easing(propOption, propOption.duration - timeExtra); //求对称时刻的值
          }
        } else if (propOption.infinity) { //仅仅是循环动画
          if (!animationObj[param].started) {
            animationObj[param].started = true
          }

          animationObj[param].now = easing(propOption, (t - propOption.delay) % propOption.duration);
        } else if (propOption.backtrace) { //仅仅是带返程的动画
          if (t <= propOption.delay + propOption.duration * 2) {
            if (!animationObj[param].started) {
              animationObj[param].started = true
            }
            animationObj[param].now = easing(propOption, propOption.duration * 2 - t + propOption.delay);
          } else {
            if (!animationObj[param].stopped) {
              animationObj[param].stopped = true
              animationObj[param].now = propOption.start;
            }
          }
        }
      } else {
        if (!animationObj[param].stopped) {
          animationObj[param].stopped = true
          animationObj[param].now = propOption.end;
        }
      }
    }
  }

  return animationObj;
}

/**
 * @param { mediacies } propOption 
 * mediacies: [[0, time1, value1], [delay2, time2, value2],..., [delayN]]
 * delayN总是相对前一个阶段的延时时间
 * timeN总是距离propOption.delay的间隔时间
 * @param {*} standardTime
 */
function easing(propOption, standardTime) {
  if (propOption.mediacies) {
    var len = propOption.mediacies.length;
    propOption.mediacies[len - 1][1] = propOption.duration;
    propOption.mediacies[len - 1][2] = propOption.end;

    var sortedIndex = -1
    for (var i = 0; i < len; i++) {
      if (standardTime <= propOption.mediacies[i][1]) {
        sortedIndex = i;
        break;
      }
    }

    if (sortedIndex !== -1) {
      var mediacy = propOption.mediacies[sortedIndex]
      var lastMediacy = propOption.mediacies[sortedIndex - 1]
      var lastMediacyValue = lastMediacy ? lastMediacy[2] : propOption.start;
      var lastMediacyTime = lastMediacy ? lastMediacy[1] : 0;
      var mediacyTime = standardTime - lastMediacyTime;
      if (mediacyTime <= mediacy[0]) {
        return lastMediacyValue;
      }
      var subPropOption = {
        start: lastMediacyValue,
        end: mediacy[2],
        duration: mediacy[1] - lastMediacyTime - mediacy[0],
        easing: propOption.easing
      }
      subPropOption.easingFuc = (propOption.getEasingFuc || easingMap[propOption.easing])(subPropOption);
      return subPropOption.easingFuc(mediacyTime - mediacy[0]);
    }
    return propOption.end;
  } else {
    return propOption.easingFuc(standardTime);
  }
}

var start = function() {
  var t = 0;
  var propsCount = 0;
  var propsCompletedCount = 0;
  var completed = false;
  var self = this;
  var option = this.option;
  var propsOption = this.propsOption;

  var animationObj = {};
  for (var param in propsOption) {
    propsCount++;
    var propOption = propsOption[param];
    var start = propOption.start;
    var end = propOption.end;
    animationObj[param] = {
      config: propOption,
      start: start,
      end: end
    }
  }

  var step = function() {
    for (var param in propsOption) {
      var propOption = propsOption[param];
      
      if (t < propOption.delay) { //动画未到时间
        animationObj[param].now = propOption.start;
      } else if (t > propOption.delay + propOption.duration) { //动画已放完
        if (!animationObj[param].stopped) {
          animationObj[param].stopped = true
          animationObj[param].now = propOption.end;
          propsCompletedCount++;
        }
      } else {
        if (!animationObj[param].started) {
          animationObj[param].started = true
        }
        animationObj[param].now = propOption.easingFuc(t);
      }
    }

    option.step && option.step(animationObj)

    if (propsCompletedCount === propsCount) {
      option.complete && option.complete()
      return;
    } else {
      t += FRAME60
      requestAnimationFrame(step);
    }
  }
  step();
}

function isInfinity() {
  var propsOption = this.propsOption;
  for (var param in propsOption) {
    if (propsOption[param].infinity) {
      return true;
    }
  }
  return false;
}

animation.prototype = {
  constructor: animation,
  start: start,
  getStaticFrame: getStaticFrame,
  isInfinity: isInfinity
}

animation.addEF = function(name, easingFun) {
  easingMap[name] = easingFun;
}

animation.FRAME60 = FRAME60;
animation.TYPE = {
  STATIC_FRAME: STATIC_FRAME,
  ANIMATION_ONCE: ANIMATION_ONCE,
  ANIMATION_LOOP: ANIMATION_LOOP
}

return animation

}))
